<?php

/**
 * Erra_System_PageBuilder - Routing class
 *
 * @package
 * @author Alexis Eric
 * @copyright Copyright (c) 2012 - Erra Prod
 * @version $1.0$
 * @access public
 */
class Erra_System_PageBuilder {

    public static $pageList = array();

    /**
     * Erra_System_PageBuilder::_getSQLPageList() - Returns SQL list for pages
     *
     * @param mixed $table
     * @return
     */
    public static function _getSQLPageList($table) {
        $list = Erra_Core_DatabasePdo::_SELECT('SELECT * FROM ' . $table);
        $result = array();

        if (Erra_Core_DatabasePdo::rowCount()) {
            while ($row = $list->fetch()) {
                if (empty($row['page_url']) || empty($row['layoutFile']) || empty($row['viewRegion'])) {
                    continue;
                }
                $row['viewRegion'] = Erra_Serialization_SerializerJson::unserializeRaw($row['viewRegion']);

                foreach ($row['viewRegion'] as $key => $element) {
                    if ($element['region_name'] == '' || empty($element['region_name']))
                        unset($row['viewRegion'][$key]);
                }
                $result[] = $row;
            }
        }
        return $result;
    }

    /**
     * PageBuilder::_getCurrentPageName() - Returns current machine pagename based on server URI
     *
     * @return
     */
    public static function _getCurrentPageName($part_num = null) {
        $langs = array_keys(Erra_Core_Locale::getAvailableLangs());
        $path = str_replace($_SERVER['SCRIPT_NAME'], '', $_SERVER['REQUEST_URI']);
        $relative = str_replace(config_get('http_host'), '', config_get('application.http_webroot'));
        if (strlen($relative)) {
            $path = str_replace($relative, '', $path);
        }
        $array = explode('/', $path);
        if (is_array($array)) {
            $array = array_values(array_filter($array));
            if (is_array($array) && count($array) > 0) {
                if (in_array($array[0], $langs)) {
                    unset($array[0]);
                }
            }
            $uri = implode('/', $array);
            $path = $uri;
        }

        if (is_numeric($part_num)) {
            $routeArray = (strstr($path, '/') !== FALSE) ? explode('/', $path) : array($path);
            return isset($routeArray[$part_num]) ? $routeArray[$part_num] : $routeArray[0];
        }
        return $path;
    }

    /**
     * PageBuilder::makeRedirection() - Builds a redirection
     *
     * @param mixed $path
     * @param mixed $lang
     * @return
     */
    public static function makeRedirection($path, $lang = LANG) {
        $path = self::makeLink($path, $lang);
        $path = str_replace('http://' . $_SERVER['HTTP_HOST'], '', $path);
        return $path;
    }

    /**
     * PageBuilder::changeLang() - Dynamically changes language
     *
     * @param mixed $langTarget
     * @return
     */
    public static function changeLang($langTarget) {
        $path = str_replace($_SERVER['SCRIPT_NAME'], '', $_SERVER['REQUEST_URI']);
        $array = explode('/', $path);
        if (is_array($array)) {
            $array = array_values(array_filter($array));
            unset($array[0]);
            $uri = implode('/', $array);
            $path = 'http://' . $_SERVER['HTTP_HOST'] . '/' . $langTarget . '/'; // . $uri;
        }

        $path = (substr($path, - 1) != "/") ? $path . '/' : $path;
        return $path;
    }

    /**
     * PageBuilder::makeLink() - Builds a link based on input
     *
     * @param mixed $path - Can be a view machine name with parameters (eg: showcase/websites) or an absolute URL
     * @param mixed $lang
     * @return
     */
    public static function makeLink($path, $lang = LANG, $ajax = false) {
        if ($path == '#')
            return '#';
        if (empty($path) || strtolower($path) == 'application.http_webroot' || $path == 'default')
            return config_get("application.http_webroot");

        if (strstr($path, 'http') != false || strstr($path, 'www') != false) {
            return $path;
        }
        // SEO friendly : if we can find a link based on language, use it
        $path = strstr('/', $path) ? explode('/', $path) : array($path);
        $pageObject = self::getPageParameters($path, self::$pageList);
        if (is_array($pageObject) && !empty($pageObject['aliases'])) {
            $path = explode('/', $pageObject['aliases']);
        }
        $path = implode('/', $path);
        $path = strstr($path, 'GET_') ? strstr($path, 'GET_', true) : $path;


        $lang = ($lang == Erra_Core_Locale::getDefaultLang()) ? '' : $lang . '/'; // Add language only if its not default lang
        if ($ajax == true) {
            $link = config_get("application.http_webroot") . '/' . $lang . '#!/' . $path;
        } else {
            $link = config_get("application.http_webroot") . '/' . $lang . $path;
        }

        $link = trim($link, '/');
        return $link;
    }

    public static function makeAjaxLink($path, $lang = LANG) {
        if (config_get('ajax_mode')) {
            return self::makeLink($path, $lang, true);
        }
        return self::makeLink($path, $lang);
    }

    public static function friendlyTitle($path) {
        // EG : 'products/GET_Category/GET_Subcategory' returns 'products' in english, 'produits' for france
        $machineName = strstr('/', $path) ? explode('/', $path) : array($path);
        $pageObject = self::getPageParameters($machineName, self::$pageList);
        if (is_array($pageObject) && !empty($pageObject['aliases'])) {
            $machineName = explode('/', $pageObject['aliases']);
        }
        $machineName = strstr($machineName[0], 'GET_') ? strstr($machineName[0], 'GET_', true) : $machineName[0];
        $machineName = trim($machineName, '/');
        return strlen($pageObject['menu_name']) ? $pageObject['menu_name'] : $machineName;
    }

    public static function friendlyMachineName($path) {
        // EG : 'products/GET_Category/GET_Subcategory' returns 'products' in english, 'produits' for france
        $path = strstr('/', $path) ? explode('/', $path) : array($path);
        $pageObject = self::getPageParameters($path, self::$pageList);
        if (is_array($pageObject) && !empty($pageObject['aliases'])) {
            $path = explode('/', $pageObject['aliases']);
        }
        return $path[0];
    }

    /**
     * PageBuilder::getCurrentPage() - Returns current page object
     *
     * @return
     */
    public static function getCurrentPage() {
        if (is_array(self::$pageList)) {
            $current_url = self::_getCurrentPageName();
            $current_url = (strstr($current_url, '/') !== false) ? explode('/', $current_url) : array($current_url);
            // Are we on homepage ?
            if (is_array($current_url) && empty($current_url[0])) {
                foreach (self::$pageList as $page) {
                    if ($page['routeName'] == 'default') {
                        return $page;
                    }
                }
            }
            foreach (self::$pageList as $page) {
                $url = $page['routeName'];
                $aliases = $page['aliases'];
                $array_url = (strstr($url, '/') !== false) ? explode('/', $url) : array($url);
                $aliases = (strstr($aliases, '/') !== false) ? explode('/', $aliases) : array($aliases);
                if ($array_url[0] == $current_url[0] || $aliases[0] == $current_url[0]) {
                    return $page;
                }
            }
        }
        return self::addPage('default', 'default', null, null, null, null);
    }

    /**
     * PageBuilder::getCurrentPageName() - Returns current machine pagename based on route
     *
     * @return
     */
    public static function getCurrentPageName($part_num = null) {
        $page = self::getCurrentPage();
        if (is_array($page) && isset($page['routeName'])) {
            if (is_numeric($part_num)) {
                $routeArray = (strstr($page['routeName'], '/') !== FALSE) ? explode('/', $page['routeName']) : array($page['routeName']);
                // returns 0 by default
                return isset($routeArray[$part_num]) ? $routeArray[$part_num] : $routeArray[0];
            }
            return $page['routeName'];
        }
        return '';
    }

    /**
     * Erra_System_PageBuilder::getPageList() - No late static bindings on PHP < 5.3, so we return pages by expliciting classes
     *
     * @return
     */
    public static function getPageList() {
        $cache_path = Erra_Core_Lexer::getCleanPath(DOCUMENT_ROOT . '/' . config_get("path.public_directory") . '/__cache/compilated_page_list.cache');

        //if (config_get('production_mode') == 0) {
        $pageList = self::_getSQLPageList('ufm__pages');
        $object = new Base_LayoutObject('idPageList');
        foreach ($pageList as $aPage) { 
            $array_views = array();
            $object->setRawData($aPage);
            foreach ($aPage['viewRegion'] as $regions) {
                $viewName = $regions['view_name'];
                if (isset($regions['view_param']))
                    $viewName .= '/' . $regions['view_param'];
                $array_views[$viewName] = $regions['region_name'];
            }
            $newElement = self::addPage($aPage['page_url'], $aPage['layoutFile'], $array_views, $object->page_aliases->getRawData(), $object->page_seo_title->getRawData(), $object->page_seo_desc->getRawData(), $object->page_seo_keywords->getRawData(), $object->ac_grant->getRawData(), $object->ac_on_deny->getRawData(), $object->menu_name->getRawData());
            $pages[] = $newElement;
        }
        // Config page is hardcoded
        $newElement = self::addPage('config/GET_Mode/GET_Target/GET_Extra/GET_More', 'Admin_DefaultLayout.phtml', array('Admin_Main.phtml/GET_Mode/GET_Target/GET_Extra' => 'content'));
        $pages[] = $newElement;
        $newElement = self::addPage('preview/GET_File', 'DefaultLayout.phtml', array('Admin_Preview.phtml/GET_File' => 'content'));
        $pages[] = $newElement;
        return $pages;
    }

    /**
     * Erra_System_PageBuilder::getLayoutList() -  No late static bindings on PHP < 5.3, so we return layouts by expliciting classes
     *
     * @return
     */
    public static function getLayoutList() {
        $layouts = array();
        $layouts[] = self::addLayout('DefaultLayout.phtml', array('Home.phtml' => 'content'));
        $layouts[] = self::addLayout('Admin_DefaultLayout.phtml', array('Admin_Main.phtml' => 'content'));
        return $layouts;
    }

    /**
     * Erra_System_PageBuilder::hub() - Main rendering function
     *
     * @param mixed $args
     * @return
     */
    public static function hub($args) {
        // Remove subfolder from args if exists
        $root = str_replace('http://', '', config_get("application.http_webroot"));
        $root = explode('/', $root);
        if (is_array($root) && count($root) > 1) {
            unset($root[0]);
            $root = array_values($root);
            $args = str_replace($root, '', $args);
        }

        // Handle lang
        $langs = array_keys(Erra_Core_Locale::getAvailableLangs());
        $args_array = explode('/', $args);
        $args_array = array_values(array_filter($args_array));
        if ((isset($args_array[0]) && in_array($args_array[0], $langs)) || ($args_array[0] == 'ajaxcontent' && isset($args_array[1]) && in_array($args_array[1], $langs))) {
            $langIndex = $args_array[0] == 'ajaxcontent' ? 1 : 0;
            DEFINE('LANG', $args_array[$langIndex]);
            unset($args_array[$langIndex]);
            if (count($args_array))
                $args = implode('/', $args_array);
            else
                $args = '';
        } else {
            DEFINE('LANG', $langs[0]);
        }
        Erra_Core_Locale::set(LANG);

        $page = self::getLayout($args);
        if (strstr($args, 'ajaxcontent/')) {
            $realTarget = self::getLayout(str_replace('ajaxcontent/', '', $args));
            $realTarget->tpl_file = 'AjaxLayout.phtml';
            $realTarget->pageParameters['layoutName'] = 'AjaxLayout.phtml';
            $page = $realTarget;
            $page->ajaxcontent = true;
        }



        // Check ACL
        if (strlen($page->pageParameters['grant'])) {
            $grantList = explode(',', $page->pageParameters['grant']);
            $access_granted = false;
            if (is_array($grantList) && count($grantList) > 0) {
                foreach ($grantList as $itemCount => $access_to_check) {
                    if (Erra_System_AccessControl::is(trim($access_to_check))) {
                        $access_granted = true;
                    }
                }
            }
            if ($access_granted == false) {
                if (strlen($page->pageParameters['on_deny'])) {
                    $redirect = self::makeLink($page->pageParameters['on_deny']);
                    return self::redirect($redirect);
                } else {
                    $url = config_get('application.http_webroot') . '/' . implode($page->pageParameters['realRoute'], '/');
                    Erra_Core_Kernel::fatalError('You don\'t have the right to access to this URL schema `' . $url . '`<br/>, and no `onDeny` parameter was specified to take proper action in this case.<br/>Set a value for this parameter on page configuration');
                    exit();
                }
            }
        }

        // Include all meta available at this stage
        Erra_Core_HtmlStream::$title = $page->pageParameters['title'];
        Erra_Core_HtmlStream::$meta['description'] = $page->pageParameters['desc'];
        Erra_Core_HtmlStream::$meta['keywords'] = $page->pageParameters['keywords'];


        echo $page->displayLayout($args);
    }

    /**
     * Erra_System_PageBuilder::redirect() - Redirects pages with header()
     *
     * @param mixed $path
     * @return
     */
    public static function redirect($path) {
        $target = self::makeLink($path);
        header('Location: ' . $target);
    }

    /* -----------------------------------------------------------------------------
      INTERNAL FUNCTIONS
      ----------------------------------------------------------------------------- */

    /**
     * Erra_System_PageBuilder::addPage() - Adds a page to route list
     *
     * @param mixed $routeName
     * @param mixed $layout
     * @param mixed $views
     * @param mixed $aliasesAssoc
     * @param mixed $titleAssoc
     * @param mixed $descAssoc
     * @return
     */
    public static function addPage($routeName, $layout, $views = null, $aliasesAssoc = null, $titleAssoc = null, $descAssoc = null, $keyAssoc = null, $grantAssoc = null, $denyAssoc = null, $menuAssoc = null) {
        $route = isset($views) ? array('routeName' => $routeName, 'layoutName' => $layout, 'pageViews' => $views) : array('routeName' => $routeName, 'layoutName' => $layout);
        $localParams = array('aliases' => $aliasesAssoc, 'title' => $titleAssoc, 'desc' => $descAssoc, 'keywords' => $keyAssoc, 'grant' => $grantAssoc, 'on_deny' => $denyAssoc, 'menu_name' => $menuAssoc);

        return array_merge($route, $localParams);
    }

    /**
     * Erra_System_PageBuilder::addLayout() - Adds a layout to stack
     *
     * @param mixed $layoutName
     * @param mixed $defaultViews
     * @return
     */
    public static function addLayout($layoutName, $defaultViews) {
        $layout = array('layoutName' => $layoutName, 'defaultViews' => $defaultViews);
        return $layout;
    }

    /**
     * Erra_System_PageBuilder::getPageParameters() - Returns an associative array containing parameters from the pages
     * designed by urlArgsArray (returns pages by default if urlArgsArray doesn't point to any page)
     *
     * @param mixed $urlArgsArray
     * @param mixed $pageList
     * @return
     */
    public static function getPageParameters($urlArgsArray, $pageList) {
        $defaultPage = '';
        $routePath = '';

        $candidatePages = array();
        $numCharMatches = 0;
        $bestPage = null;
        // get URL string
        $currentUrl = implode('/', $urlArgsArray);
        // Remove machine arguments for matching
        $currentUrl = strstr($currentUrl, 'GET_') ? strstr($currentUrl, 'GET_', true) : $currentUrl;

        // First, get default page
        foreach ($pageList as $aPage) {
            if ($aPage['routeName'] == 'default') {
                $defaultPage = $aPage;
                $defaultPage['realRoute'] = array();
            }
        }
        // Check in each page if URL matches without aliases
        foreach ($pageList as $aPage) {
            $pageRouteNoParams = array();
            $pageRouteArray = strstr($aPage['routeName'], '/') ? explode('/', $aPage['routeName']) : array($aPage['routeName']);
            foreach ($pageRouteArray as $pageElement) {
                if (!strstr($pageElement, 'GET'))
                    $pageRouteNoParams[] = $pageElement;
            }
            $pageRouteNoParams = implode('/', $pageRouteNoParams);
            if (empty($pageRouteNoParams))
                continue;
            // We want an 100% matching
            if (strstr('/' . $currentUrl . '/', '/' . $pageRouteNoParams . '/') && strpos('/' . $currentUrl . '/', '/' . $pageRouteNoParams . '/') == 0) {
                if (strlen((string) $pageRouteNoParams) > $numCharMatches) {
                    $bestPage = $aPage;
                    $numCharMatches = strlen((string) $pageRouteNoParams);
                }
            }
        }
        // Then, check in each page if URL matches with aliases
        foreach ($pageList as $aPage) {
            if (empty($aPage['aliases']))
                continue;
            // on reconstruit l'url avec l'alias
            $alias = str_replace('/', '', $aPage['aliases']);
            $pageRouteNoParams = array();
            $pageRouteArray = strstr($aPage['routeName'], '/') ? explode('/', $aPage['routeName']) : array($aPage['routeName']);
            $pageRouteArray[0] = $alias;

            foreach ($pageRouteArray as $pageElement) {
                if (!stristr($pageElement, 'param'))
                    $pageRouteNoParams[] = $pageElement;
            }
            $pageRouteNoParams = implode('/', $pageRouteNoParams);
            if (empty($pageRouteNoParams))
                continue;
            // On veut un matching à 100%

            $pageRouteNoParams = strstr($pageRouteNoParams, 'GET_') ? strstr($pageRouteNoParams, 'GET_', true) : $pageRouteNoParams;
            if (strstr('/' . $currentUrl . '/', '/' . $pageRouteNoParams . '/') && strpos('/' . $currentUrl . '/', '/' . $pageRouteNoParams . '/') == 0) {
                if (strlen((string) $pageRouteNoParams) > $numCharMatches) {
                    $bestPage = $aPage;
                    $numCharMatches = strlen((string) $pageRouteNoParams);
                }
            }
        }
        if (is_null($bestPage))
            return $defaultPage;
        // test if url without parameter matches with real url
        $pageRouteNoParams = array();
        $pageRouteArray = strstr($bestPage['routeName'], '/') ? explode('/', $bestPage['routeName']) : array($bestPage['routeName']);

        foreach ($pageRouteArray as $pageElement) {
            if (!stristr($pageElement, 'param'))
                $pageRouteNoParams[] = $pageElement;
        }

        $pageRouteNoParams = implode('/', $pageRouteNoParams);
        $currentUrl = implode('/', $urlArgsArray);

        $routeParams = explode('/', $bestPage['routeName']);
        $realRouteParams = $urlArgsArray;

        if (count($realRouteParams) < count($routeParams))
            $realRouteParams = array_pad($realRouteParams, count($routeParams), '');
        else if (count($routeParams) < count($realRouteParams))
            $routeParams = array_pad($routeParams, count($realRouteParams), '');

        $route = (is_array($realRouteParams) && is_array($routeParams)) ? array_combine($routeParams, $realRouteParams) : array();
        $bestPage['realRoute'] = $route;
        return $bestPage;
    }

    /**
     * Erra_System_PageBuilder::clearParameters() - Cleans parameters list
     *
     * @param mixed $routeArray
     * @return
     */
    public static function clearParameters(&$routeArray) {
        $result = array();
        foreach ($routeArray as $routeKey => $routeValue) {
            $newKey = str_replace('GET_', '', $routeKey);
            $result[$newKey] = $routeValue;
        }
        extract($routeArray);

        return $result;
    }

    /**
     * Erra_System_PageBuilder::getLayoutParameters() - Returns layout parameters
     *


     * @param mixed $layoutName
     * @return
     */
    public static function getLayoutParameters($layoutName) {
        $layoutList = self::getLayoutList();
        foreach ($layoutList as $aLayout) {
            if ($aLayout['layoutName'] == $layoutName)
                return $aLayout;
        }
        return isset($pageList['default']) ? $pageList['default'] : null;
    }

    /**
     * Erra_System_PageBuilder::filterRouteAndAliases() - Returns only route and aliases from page
     *
     * @param mixed $array
     * @return
     */
    public static function filterRouteAndAliases($array) {
        $result = array();
        foreach ($array as $aPage) {
            $result[] = array('routeName' => $aPage['routeName'], 'aliases' => $aPage['aliases']);
        }
        return $result;
    }

    /**
     * Erra_System_PageBuilder::getLayout() - Returns a layout
     *
     * @param mixed $args
     * @return
     */
    public static function getLayout($args) {
        // Create layout from url route or from page id
        $pageList = self::getPageList();
        if (is_numeric($args))
            $pageParams = self::getPageFromId($args);
        else
            $pageParams = self::getPageParameters(Erra_Core_HtmlStream::getArgs($args), $pageList);


        $layoutFileName = $pageParams['layoutName'];

        $layout = new Erra_Templating_Layout($layoutFileName);
        // Set default views
        $layoutParams = self::getLayoutParameters($pageParams['layoutName']);

        if (isset($layoutParams['defaultViews'])) {
            foreach ($layoutParams['defaultViews'] as $viewName => $regionName) {
                $layout->views[$regionName] = $viewName;
            }
        }
        $layout->pageParameters = $pageParams;
        self::$pageList = $pageList;
        // Assign views and set their parameters from url route

        foreach ($pageParams['pageViews'] as $viewName => $regionName) {
            $viewArray = explode('/', $viewName);
            $viewClass = $viewArray[0];
            $viewName = $viewArray[0];
            $layout->views[$regionName] = $viewName;

            $paramList = array_slice($viewArray, 1);

            $list = array();
            // First assign default view values
            foreach ($paramList as $parameter) {
                if (strstr($parameter, '=') !== false) {
                    $paramAndValue = explode('=', $parameter);
                    $list[$paramAndValue[0]] = $paramAndValue[1];
                }
            }
            // Then try to retreive from url other values
            foreach ($paramList as $parameter) {
                if (strstr($parameter, '=') !== false) {
                    $paramAndValue = explode('=', $parameter);
                    $parameter = $paramAndValue[0];
                }
                $list_parameter = (isset($list[$parameter])) ? $list[$parameter] : '';
                $list[$parameter] = isset($pageParams['realRoute'][$parameter]) ? $pageParams['realRoute'][$parameter] : $list_parameter;
            }
            $list = self::clearParameters($list);
            $layout->view_parameters[$regionName] = $list;
        }
        foreach ($layout->views as $regionName => $viewClass) {
            $viewClass = $viewClass;
            $layout->views[$regionName] = new Erra_Templating_View($viewClass);
        }
        // The layout is ready !
        return $layout;
    }

}

?>